123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569 |
- <template>
- <div
- class="max-w-screen-xl content my-0 mx-auto text-size-base overflow-hidden"
- :class="!detail ? 'flex flex-column justify-center items-center h-full' : ''"
- >
- <div v-if="detail">
- <div class="breadcrumb my-[30px] pb-[20px] bb-1px_#EBEBEB" role="navigation">
- <n-breadcrumb separator=">" v-if="breadcrumb">
- <n-breadcrumb-item @click="$router.push('/')">{{ $t('home') }}</n-breadcrumb-item>
- <template v-for="(bread, index) in breadcrumb" :key="index">
- <n-breadcrumb-item @click="handleBreadcrumb(bread)">
- {{ bread.title }}</n-breadcrumb-item
- >
- </template>
- </n-breadcrumb>
- </div>
- <div v-if="currentCate">
- <n-h2 class="doc-title mb-10"> {{ currentCate.title }}</n-h2>
- </div>
- <div class="waper-content w-full flex flex-row flex-nowrap">
- <div
- class="waper-menu flex-basis-[240px] flex-grow-0 flex-shrink-0 br-1px_#EBEBEB overflow-hidden"
- :class="{ open: isOpenMuen }"
- >
- <div class="open-btn" @click="toggleOpen">
- <svg
- xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- viewBox="0 0 512 512"
- >
- <path
- fill="none"
- stroke="currentColor"
- stroke-linecap="round"
- stroke-linejoin="round"
- stroke-width="32"
- d="M96 256h320"
- ></path>
- <path
- fill="none"
- stroke="currentColor"
- stroke-linecap="round"
- stroke-linejoin="round"
- stroke-width="32"
- d="M96 176h320"
- ></path>
- <path
- fill="none"
- stroke="currentColor"
- stroke-linecap="round"
- stroke-linejoin="round"
- stroke-width="32"
- d="M96 336h320"
- ></path>
- </svg>
- </div>
- <n-tree
- class="left-tree"
- v-model:selected-keys="selectedKeys"
- v-if="mainCategories"
- block-line
- :data="mainCategories.children"
- :default-expanded-keys="defaultExpandedKeys"
- :node-props="nodeProps"
- key-field="id"
- label-field="title"
- children-field="children"
- selectable
- >
- <template #empty>
- <n-empty :description="$t('no_data')" />
- </template>
- </n-tree>
- </div>
- <div class="infos flex-1 pages w-[calc(100%-80px)] px-[40px] mb-[80px] overflow-hidden">
- <div class="box bb-1px_#EBEBEB color-[#999999]">
- <n-h2 class="font-700"> {{ detail.title }}</n-h2>
- <span class="flex flex-row gap-col-6 pb-[15px]">
- <p class="time">
- {{ $t('publish') }} {{ dayjs(detail.createTime).format('YYYY-MM-DD') }}
- </p>
- <!-- <p>{{ $t('read') }} ( {{ detail.readCount }} )</p> -->
- </span>
- </div>
- <div class="w-full content-html" v-html="detail.content"></div>
- <div class="top-clt w-full flex justify-between pt-6 bt-1px_#EBEBEB color-[#999898]">
- <div class="prev flex flex-col">
- <span
- class="flex-1 text-align-left"
- :class="{ articleNear: articleNear[0] }"
- @click="handleToArticle(articleNear[0])"
- >
- {{ $t('prev_article') }}
- </span>
- <span
- class="flex-1 articleNear"
- v-if="articleNear[0]"
- @click="handleToArticle(articleNear[0])"
- >
- {{ articleNear[0].title }}
- </span>
- <span v-else>
- {{ $t('no_data') }}
- </span>
- </div>
- <div class="next flex flex-col">
- <span
- class="flex-1 text-align-right"
- :class="{ articleNear: articleNear[1] }"
- @click="handleToArticle(articleNear[1])"
- >
- {{ $t('next_article') }}
- </span>
- <span
- class="flex-1 articleNear"
- v-if="articleNear[1]"
- @click="handleToArticle(articleNear[1])"
- >
- {{ articleNear[1].title }}
- </span>
- <span v-else>
- {{ $t('no_data') }}
- </span>
- </div>
- </div>
- <n-back-top to=".pages" :visibility-height="800" />
- </div>
- <div class="desc-list flex-basis-[240px] flex-grow-0 flex-shrink-0 overflow-hidden">
- <div class="min-h-[200px] bg-[#F5F9FF] b-r-[8px] p-[20px]">
- <n-h4 class="font-600 ext-size-base">{{ $t('main_content') }}</n-h4>
- <n-anchor :show-rail="false">
- <n-anchor-link
- class="text-size-base color-[#999999]"
- v-for="(item, index) in mainContents"
- :key="index"
- :title="item.text"
- :href="`#my-section-${index + 1}`"
- >
- </n-anchor-link>
- </n-anchor>
- </div>
- </div>
- </div>
- </div>
- <div v-else>
- <n-empty :description="$t('no_article')">
- <template #extra>
- {{ $t('no_data_article') }},
- <n-button link size="small" quaternary type="primary" @click="router.push('/')">
- {{ $t('go_home') }}
- </n-button>
- </template>
- <template #default></template>
- </n-empty>
- </div>
- </div>
- </template>
- <route>
- {
- name: "showdoc",
- meta: {
- layout: "base"
- }
- }
- </route>
- <script setup lang="ts">
- import {
- type ArticleDetailType,
- type ArticleDetailMenuType,
- type CategoryItem,
- getArticleDetail,
- getArticleCount,
- getCategoryTree,
- // getArticlesByCateId,
- getNearArticles,
- } from '@/api'
- import {
- htmlToTree,
- createAnchorNames,
- dayjs,
- findNodeById,
- findBreadcrumbPath,
- type TreeNode,
- } from '@/utils'
- import {
- NH1,
- NH2,
- NH4,
- NBackTop,
- NEmpty,
- NButton,
- NBreadcrumb,
- NBreadcrumbItem,
- NTree,
- NAnchor,
- NAnchorLink,
- } from 'naive-ui'
- import type { TreeOption } from 'naive-ui'
- const route = useRoute()
- const router = useRouter()
- const selectedKeys = ref([])
- const params = route.params as {
- id?: number
- }
- const breadcrumb = ref<CategoryItem[]>([])
- console.log('route', route)
- const detail = ref<ArticleDetailType | undefined>()
- const mainContents = ref<ArticleDetailMenuType[]>([])
- const currentCate = ref<CategoryItem | undefined>()
- const mainCategories = ref<CategoryItem[]>([])
- const defaultExpandedKeys = ref<number[]>([])
- const articleNear = ref<ArticleDetailType[]>([])
- const isOpenMuen = ref<Boolean>(false)
- const toggleOpen = () => {
- isOpenMuen.value = !isOpenMuen.value
- }
- const nodeProps = ({ option }: { option: TreeOption }) => {
- return {
- async onClick() {
- console.log('option', option.id)
- router.replace(`/showcate/${option.id}`)
- setTimeout(() => {
- location.reload()
- }, 50)
- },
- }
- }
- onMounted(async () => {
- setTimeout(() => {
- const html = document.querySelector('.content-html')
- if (html) {
- createAnchorNames(html)
- }
- }, 1000)
- if (params.id) {
- if (!isNaN(+params.id)) {
- await getArticleCount(+params.id)
- }
- }
- })
- watchEffect(() => {
- if (params.id) {
- if (!isNaN(+params.id)) {
- getArticleDetail(+params.id).then(async (data) => {
- if (data.data) {
- detail.value = data.data
- document.title = detail.value.title
- mainContents.value = htmlToTree(detail.value.content)
- if (detail.value.categoryId) {
- const res = await getCategoryTree(detail.value.categoryId)
- if (res.data) {
- mainCategories.value = res.data as CategoryItem[]
- if (mainCategories.value) {
- currentCate.value = findNodeById(
- [mainCategories.value] as unknown as TreeNode[],
- detail.value.categoryId,
- ) as unknown as CategoryItem
- defaultExpandedKeys.value = [currentCate.value.parentId]
- const selectID = currentCate.value.parentId
- ? currentCate.value.id
- : currentCate.value.children[0].id
- selectedKeys.value = [selectID]
- breadcrumb.value = findBreadcrumbPath(
- [mainCategories.value] as unknown as TreeNode[],
- detail.value.categoryId,
- )
- console.log('breadcrumb', [mainCategories.value], breadcrumb.value)
- }
- }
- }
- }
- })
- getNearArticles(+params.id).then((res) => {
- if (res.data) {
- articleNear.value = res.data
- }
- })
- }
- }
- })
- const handleToArticle = (article: ArticleDetailType) => {
- console.log('article', article)
- if (!article) {
- return
- }
-
- router.replace(`/showdoc/${article.id}`)
- setTimeout(() => {
- location.reload()
- }, 100)
- }
- const handleBreadcrumb = (bread: TreeNode) => {
- router.replace(`/showcate/${bread.id}`)
- setTimeout(() => {
- location.reload()
- }, 50)
- }
- </script>
- <style lang="scss" scoped>
- :deep(.content-html img) {
- // width: 100% !important;
- height: auto;
- }
- .pages {
- position: relative;
- padding-bottom: 110px;
- }
- :deep(.n-back-top) {
- // position: absolute;
- // bottom: 0;
- }
- :deep(.left-tree) {
- --n-node-content-height: 40px !important;
- //--n-node-color-hover: rgba(6,97,201,0.06) !important;
- //border-right: 1px solid #e5e7eb;
- .n-tree-node-wrapper {
- width: 240px;
- font-size: 16px;
- }
- }
- .articleNear {
- &:hover {
- cursor: pointer;
- color: #5a5a5a;
- }
- }
- .open-btn {
- display: none;
- svg {
- width: 100%;
- height: 100%;
- }
- }
- [is-mobile] {
- .breadcrumb {
- height: 0.88rem;
- padding: 0;
- margin: 0;
- display: flex;
- align-items: center;
- justify-content: flex-start;
- padding: 0 0.5333rem;
- overflow-x: auto;
- :deep(.n-breadcrumb) {
- ul {
- display: flex;
- align-items: center;
- justify-content: flex-start;
- }
- }
- }
- .doc-title {
- font-weight: bold;
- font-size: 0.3733rem;
- color: #000000;
- line-height: 0.44rem;
- padding: 0.5333rem;
- box-sizing: border-box;
- margin: 0;
- }
- .content {
- min-height: auto;
- }
- .waper-content {
- display: block;
- position: relative;
- flex-direction: column;
- :deep(.n-back-top) {
- right: 0.1333rem !important;
- width: 0.8rem !important;
- height: 0.8rem !important;
- padding: 0 !important;
- min-width: auto !important;
- .n-base-icon {
- font-size: 0.5333rem !important;
- }
- }
- .waper-menu {
- position: fixed;
- left: 0;
- top: 5.0667rem;
- z-index: 1;
- background: #fff;
- width: 6.4rem;
- height: 30%;
- border-right: 1px #ebebeb solid;
- border-top: 1px #ebebeb solid;
- border-bottom: 1px #ebebeb solid;
- overflow: visible;
- transition: all 0.3s;
- transform: translateX(-100%);
- &.open {
- transform: translateX(0);
- }
- :deep(.left-tree) {
- // --n-node-content-height: 40px !important;
- // //--n-node-color-hover: rgba(6,97,201,0.06) !important;
- // //border-right: 1px solid #e5e7eb;
- width: 100%;
- height: 100%;
- overflow-y: auto;
- .n-tree-node-wrapper {
- width: 100%;
- padding: 0;
- }
- }
- .open-btn {
- position: absolute;
- right: calc(-0.8rem - 1px);
- top: -1px;
- width: 0.8rem;
- height: 0.8533rem;
- border-radius: 0px 0.1067rem 0.1067rem 0px;
- border: 1px solid #ebebeb;
- display: block;
- background: #fff;
- z-index: 2;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- }
- .infos {
- width: 100%;
- padding: 0 0.8rem;
- box-sizing: border-box;
- margin-bottom: 1.6rem;
- .box {
- font-weight: 500;
- font-size: 0.5333rem !important;
- color: #000000;
- line-height: 0.8rem;
- .time {
- font-size: 0.2667rem;
- color: #999999;
- line-height: 0.3067rem;
- }
- }
- :deep(.content-html) {
- padding: 0.8rem 0;
- h1 {
- font-weight: bold;
- font-size: 0.4rem;
- color: #000000;
- line-height: 0.48rem;
- margin: 0.2667rem 0;
- }
- h2 {
- font-weight: bold;
- font-size: 0.36rem;
- color: #000000;
- line-height: 0.48rem;
- margin: 0.2667rem 0;
- }
- h3,
- h4,
- h5,
- h6 {
- margin: 0.2667rem 0;
- font-weight: bold;
- font-size: 0.32rem;
- color: #000000;
- line-height: 0.48rem;
- }
- p {
- font-weight: 400;
- font-size: 0.32rem;
- color: #000000;
- line-height: 0.48rem;
- }
- video {
- width: 100%;
- height: auto;
- }
- img {
- // width: auto;
- // height: auto;
- max-height: 100%;
- max-width: 100%;
- }
- }
- .top-clt {
- flex-direction: row;
- justify-content: space-between;
- padding-top: 0.8rem;
- .prev {
- font-size: 0.2667rem;
- display: flex;
- width: 45%;
- justify-content: flex-start;
- .articleNear {
- width: 100%;
- font-size: 0.32rem;
- color: #999898;
- line-height: 0.3733rem;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
- span {
- font-size: 0.32rem;
- color: #999898;
- // line-height: 0.3733rem;
- text-align: left;
- width: 100%;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
- }
- .next {
- font-size: 0.2667rem;
- display: flex;
- width: 45%;
- justify-content: flex-end;
- span {
- font-size: 0.32rem;
- color: #999898;
- // line-height: 0.3733rem;
- text-align: right;
- width: 100%;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
- }
- }
- }
- }
- .desc-list {
- padding: 0 0.8rem 1.6rem;
- > div {
- padding: 0.8rem 0.4267rem;
- min-height: auto;
- .ext-size-base {
- font-weight: bold;
- font-size: 0.3733rem;
- color: #000000;
- line-height: 0.44rem;
- }
- .n-anchor {
- font-weight: 400;
- font-size: 0.32rem;
- color: #333333;
- line-height: 0.3733rem;
- }
- }
- }
- }
- </style>
|